package com.basic.utils;

import cn.hutool.core.util.StrUtil;
import com.basic.exception.GlobalException;
import com.basic.pojo.MyTableFieldInfo;
import com.basic.service.dto.GenParam;
import org.apache.velocity.VelocityContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;

import java.lang.reflect.Field;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class GenUtil {

    private static Map<String, JdbcTemplate> jdbcTemplateMap = new HashMap<>();

    private static String columnInfoTemplateSql = "select COLUMN_NAME,DATA_TYPE,COLUMN_TYPE,IS_NULLABLE,COLUMN_KEY,PRIVILEGES,COLUMN_COMMENT from INFORMATION_SCHEMA.COLUMNS where TABLE_SCHEMA = \"{}\" AND TABLE_NAME = \"{}\"";

    private static String tableInfoSql = "SELECT table_name name,TABLE_COMMENT value FROM INFORMATION_SCHEMA.TABLES WHERE table_name = '{}' and table_schema = '{}'";

    public static JdbcTemplate getTemplate(String url) {
        return jdbcTemplateMap.get(url.substring(0, url.indexOf("?")));
    }

    public static JdbcTemplate createJdbcTemplate(String driverClass, String url, String username, String passwd) {
        final String key = url.substring(0, url.indexOf("?"));
        JdbcTemplate template = jdbcTemplateMap.get(key);
        if (template != null) {
            return template;
        }
        final DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(driverClass);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(passwd);
        template = new JdbcTemplate(dataSource);
        jdbcTemplateMap.put(key, template);

        return template;
    }

    public static Map<String, GenParam.TableInfo> getAllDataForDatasource(JdbcTemplate jdbcTemplate, String databaseName) throws IllegalAccessException {
        return getAllDataForDatasource(jdbcTemplate, jdbcTemplate.queryForList("show tables", String.class), databaseName);
    }

    public static Map<String, GenParam.TableInfo> getAllDataForDatasource(JdbcTemplate jdbcTemplate, List<String> showTables, String databaseName) throws IllegalAccessException {

        Map<String, GenParam.TableInfo> allDatabaseData = new HashMap<>();
        for (String each : showTables) {
            List<Map<String, Object>> maps = jdbcTemplate.queryForList(StrUtil.format(columnInfoTemplateSql, databaseName, each));
            List<MyTableFieldInfo> tableFieldInfoList = new ArrayList<>();
            for (Map<String, Object> map : maps) {

                final MyTableFieldInfo myTableFieldInfo = new MyTableFieldInfo();
                final Field[] declaredFields = myTableFieldInfo.getClass().getDeclaredFields();
                for (Map.Entry<String, Object> entry : map.entrySet()) {
                    Field fieldForColumn = getFieldForColumn(declaredFields, entry.getKey());
                    if (fieldForColumn != null) {
                        fieldForColumn.setAccessible(true);
                        fieldForColumn.set(myTableFieldInfo, entry.getValue());
                    }
                }
                myTableFieldInfo.setColumnNameSmall(StrUtil.toCamelCase(myTableFieldInfo.getColumnName()));
                myTableFieldInfo.setClassType(mapDatabaseTypeToJavaType(myTableFieldInfo.getDataType()));
                tableFieldInfoList.add(myTableFieldInfo);
            }

            final GenParam.TableInfo tableInfo = GenParam.getTableInfo(each, String.valueOf(jdbcTemplate.queryForList(StrUtil.format(tableInfoSql, each, databaseName)).get(0).get("value")));
            tableInfo.setFieldInfos(tableFieldInfoList);
            allDatabaseData.put(each, tableInfo);
        }


        return allDatabaseData;
    }

    private static Field getFieldForColumn(Field[] declaredFields, String key) {
        for (Field field : declaredFields) {
            if (field.getName().equals(StrUtil.toCamelCase(key.toLowerCase()))) {
                return field;
            }
        }

        return null;
    }


    // 映射数据库类型到Java类型的辅助方法
    public static String mapDatabaseTypeToJavaType(String databaseTypeName) {
        switch (databaseTypeName.toUpperCase()) {
            case "VARCHAR":
            case "CHAR":
            case "TEXT":
                return "String";
            case "INT":
            case "INTEGER":
                return "Integer";
            case "BIGINT":
                return "Long";
            case "FLOAT":
            case "REAL":
                return "Float";
            case "DOUBLE":
            case "DOUBLE PRECISION":
                return "Double";
            case "DECIMAL":
            case "NUMERIC":
                return "BigDecimal";
            case "DATE":
            case "TIME":
            case "DATETIME":
            case "TIMESTAMP":
                return "java.util.Date"; // 或 "LocalDate"
            case "BOOLEAN":
            case "BIT":
                return "Boolean"; // 注意：BIT可能映射为byte
            default:
                return "Object"; // 对于未知类型，默认使用Object
        }
    }


    /**
     * 将对象属性映射到VelocityContext中，为了后面解析模板
     * @param object
     * @return
     * @throws IllegalAccessException
     */
    public static VelocityContext createVelocityContextForClass(Object object) throws IllegalAccessException {
        VelocityContext velocityContext = new VelocityContext();
        Field[] declaredFields = object.getClass().getDeclaredFields();
        for (Field field : declaredFields) {
            field.setAccessible(true);
            Object o = field.get(object);
            if (o != null) {
                velocityContext.put(field.getName(), o);
            }
        }

        return velocityContext;
    }

    public static String getDatabaseNameForUrl(String url) {
        // 定义正则表达式来匹配 JDBC URL 并捕获数据库名
        String regex = "jdbc:mysql://[^/]+/([^/]+)\\?.*";

        // 创建 Pattern 对象
        Pattern pattern = Pattern.compile(regex);

        // 创建 Matcher 对象
        Matcher matcher = pattern.matcher(url);

        // 如果找到匹配项，则提取数据库名
        if (matcher.find()) {
            return matcher.group(1);
        } else {
            throw new GlobalException("数据库url有误");
        }
    }

}
